#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "comm.h"

struct mailbox mails[64] = {0}; // 最大存储64封邮件
int g_mail_count = 0;

void count_mailbox()
{
	char buf[128] = {0};

	// 以只读方式打开文件
	FILE *fp = fopen(MAILNAME, "r");
	if (!fp)
	{
		printf("fopen failed\n");
		return;
	}

	g_mail_count = 0;

	while (fgets(buf, sizeof(buf) - 1, fp) != NULL)
	{
		if(strncmp(buf, "Data:", 5) == 0)
		{
			fgets(buf, sizeof(buf) - 1, fp);
			for(int i = 0; i <= g_mail_count; i++)
			{
				strncpy(mails[i].mail_data, buf, sizeof(mails[i].mail_data));
			}
		}
		if (strncmp(buf, "From:", 5) == 0)
		{
			g_mail_count++;
		}
	}

	fclose(fp);
	return;
}

void *pthread_proc_pop3(void *arg)
{
	int proc_fd = *(int *)arg;
	int proc_ret;
	int flag_user = 0;
	int flag_pass = 0;
	int deleted_count = 0;
	char buffer[128] = {0};
	char response[128] = {0};
	struct sockaddr_in client_addr;
	struct timeval timeout;
	struct mailbox mail;
	fd_set read_set = {0};
	socklen_t client_len = sizeof(client_addr);
	getpeername(proc_fd, (struct sockaddr *)&client_addr, &client_len);

	// 分离线程
	pthread_detach(pthread_self());

	// 获取客户端ip
	char client_ip[32] = {0};
	inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, client_ip, sizeof(client_ip));

	// 具体流程如下
	while (1)
	{
		FD_ZERO(&read_set);
		FD_SET(proc_fd, &read_set);

		timeout.tv_sec = 5;
		timeout.tv_usec = 0;

		proc_ret = select(proc_fd + 1, &read_set, NULL, NULL, &timeout);
		if (proc_ret < 0)
		{
			perror("select failed");
			continue;
		}

		if (FD_ISSET(proc_fd, &read_set))
		{
			// 读取客户端数据
			memset(buffer, 0, sizeof(buffer));
			proc_ret = recv(proc_fd, buffer, sizeof(buffer) - 1, 0);
			if (proc_ret < 0)
			{
				perror("recv failed");
				close(proc_fd);
				return NULL;
			}

			if ((flag_user == 1) && (flag_pass == 1))
			{
				count_mailbox(); // 计算邮件数量

				if (strncasecmp(buffer, "LIST", 4) == 0) // 有多少邮件
				{
					memset(response, 0, sizeof(response));
					snprintf(response, sizeof(response), "+OK %d messages\r\n", g_mail_count);
					proc_ret = send(proc_fd, response, strlen(response), 0);
					if (proc_ret < 0)
					{
						perror("list send failed");
						continue;
					}

					for (int i = 0; i < g_mail_count; i++)
					{
						memset(response, 0, sizeof(response));
						sprintf(response, "%d %ld\r\n", i + 1, strlen(mails[i].mail_data));
						send(proc_fd, response, strlen(response), 0);
					}
					send(proc_fd, ".\r\n", 3, 0);
				}
				else if (strncasecmp(buffer, "RETR", 4) == 0) // 查看其中一份邮件
				{
					// 以只读方式打开文件
					FILE *fp = fopen(MAILNAME, "r");
					if (!fp)
					{
						printf("fopen failed\n");
						return NULL;
					}

					memset(response, 0, sizeof(response));

					while (fgets(response, sizeof(response) - 1, fp) != NULL)
					{
						send(proc_fd, response, strlen(response), 0);
						if(strrchr(response, '\n') == 0)
						{
							send(proc_fd, ".\r\n", 3, 0);
							break;
						}
					}

					fclose(fp);
				}
				else if (strncasecmp(buffer, "quit", 4) == 0)
				{
					send(proc_fd, "+OK Bye\r\n", 9, 0);

					close(proc_fd);
					return NULL;
				}
				else
				{
					send(proc_fd, "-ERR Authentication failed\r\n", 29, 0);
				}
			}
			else
			{
				if (strncmp(buffer, "USER", 4) == 0)
				{
					memset(response, 0, sizeof(response));
					sscanf(buffer, "USER %s", response);
					if (strncmp(response, USERNAME, strlen(USERNAME)) == 0)
					{
						flag_user = 1;

						if ((flag_user == 1) && (flag_pass == 1))
						{
							send(proc_fd, "+OK Welcome\r\n", 13, 0);
						}
						else
						{
							send(proc_fd, "+OK\r\n", 5, 0);
						}
					}
				}
				else if (strncmp(buffer, "PASS", 4) == 0)
				{
					memset(response, 0, sizeof(response));
					sscanf(buffer, "PASS %s", response);
					if (strncmp(response, AUTHCODE, strlen(AUTHCODE)) == 0)
					{
						flag_pass = 1;

						if ((flag_user == 1) && (flag_pass == 1))
						{
							send(proc_fd, "+OK Welcome\r\n", 13, 0);
						}
						else
						{
							send(proc_fd, "+OK\r\n", 5, 0);
						}
					}
				}
				else if (strncasecmp(buffer, "quit", 4) == 0)
				{
					send(proc_fd, "+OK Bye\r\n", 8, 0);
					break;
				}
				else
				{
					send(proc_fd, "-ERR Authentication failed\r\n", 29, 0);
				}
			}
		}
	}

	close(proc_fd);
	return NULL;
}

int main()
{
	int socket_fd, new_fd;
	int ret;
	char buf[128] = {0};
	struct sockaddr_in server_addr;
	struct sockaddr_in client_addr;
	socklen_t addrlen = sizeof(client_addr);

	// 创建套接字
	socket_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (socket_fd < 0)
	{
		perror("socket failed");
		return -1;
	}

	// 设置地址重用属性
	int reuse = 1;
	setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));

	// 绑定端口
	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = INADDR_ANY;
	server_addr.sin_port = htons(POP3_PORT);

	ret = bind(socket_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
	if (ret < 0)
	{
		perror("bind failed");
		return -1;
	}

	// 监听端口
	ret = listen(socket_fd, 5);
	if (ret < 0)
	{
		perror("listen failed");
		return -1;
	}
	printf("pop3 server listening on port: %d\n", POP3_PORT);

	while (1)
	{
		// 接受新的连接
		memset(&client_addr, 0, sizeof(client_addr));
		new_fd = accept(socket_fd, (struct sockaddr *)&client_addr, &addrlen);
		if (new_fd < 0)
		{
			perror("accept failed");
			continue;
		}

		// 发送欢迎消息
		char welcome[128] = {0};
		inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, buf, sizeof(buf));
		snprintf(welcome, sizeof(welcome), "+OK %s POP3 Server\r\n", buf);
		ret = send(new_fd, welcome, sizeof(welcome), 0);
		if (ret < 0)
		{
			perror("welcome send failed");
			continue;
		}

		// 创建线程
		pthread_t tid;
		pthread_create(&tid, NULL, pthread_proc_pop3, (void *)&new_fd);
	}

	close(socket_fd);
	return 0;
}
